/*================================================================================
// File Name: tku.v
// Author: atom
// Created Time: 2023年12月07日 星期四 22时02分53秒
//
//       *         *********       ****            *         *     
//      * *            *          *    *          * *       * *    
//     *   *           *         *      *        *   *     *   *   
//    *******          *         *      *       *     *   *     *  
//   *       *         *          *    *       *       * *       * 
//  *         *        *           ****       *         *         *
================================================================================*/
module tku(
    input                   clk,
    input                   rst_n,

    input                   req_vld,
    input       [10:0]      req_bcnt,
    output  reg             rsp_vld,
    output  reg             rsp_ack,        //0:ack   1:nack


    input                   csr_tkm_mode,
    input       [11:0]      csr_tkm_cir,    // unit is 1 Mbps so 
    input       [11:0]      csr_tkm_pir,
    input       [19:0]      csr_tkm_cbs,
    input       [19:0]      csr_tkm_pbs,
    output  reg [31:0]      csr_tkm_cbc_sta,
    output  reg [31:0]      csr_tkm_pbc_sta
);

parameter       COVERY_GAP      =       4;


reg     [19:0]      commit_bucket;
reg     [19:0]      peak_bucket;

wire    [13:0]      commit_covery_token_num;
wire    [13:0]      peak_covery_token_num;

reg     [1:0]       timer;
wire                token_plus_tag;

wire    [13:0]      commit_token_plus;
wire    [13:0]      peak_token_plus;

wire    [10:0]      commit_token_dec;
wire    [10:0]      peak_token_dec;


wire                one_bucket_mode;
wire                two_bucket_mode;

wire                one_bucket_ack;
wire                one_bucket_nack;
wire                two_bucket_cbucket_ack;
wire                two_bucket_pbucket_ack; 
wire                two_bucket_ack;
wire                two_bucket_nack;

reg     [10:0]      req_bcnt_d;



function [19:0] func_bucket_cal;
    input   [19:0]  bucket_raw;
    input   [13:0]  bucket_plus;
    input   [10:0]  bucket_dec;
    input   [19:0]  bucket_thres;

    begin
         func_bucket_cal[19:0] = (bucket_raw[19:0] + bucket_plus[13:0] - bucket_dec[10:0]) > bucket_thres[19:0] ? bucket_thres[19:0] :
                                        bucket_raw[19:0] + bucket_plus[13:0] - bucket_dec[10:0] ;
    end
endfunction

/*=======================covery ability==========================
clk is 100M, means 10ns toggle once, but token unit is 8ns
so merge in 40ns add 5 times token
===============================================================*/
assign commit_covery_token_num[13:0] = {2'b0,csr_tkm_cir[11:0]}<<2 + {2'b0,csr_tkm_cir[11:0]}<<1;
assign peak_covery_token_num[13:0]   = {2'b0,csr_tkm_pir[11:0]}<<2 + {2'b0,csr_tkm_pir[11:0]}<<1;


always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        timer[1:0] <= 2'b0;
    else if(token_plus_tag)
        timer[1:0] <= 2'b0;
    else
        timer[1:0] <= timer[1:0] + 2'b1;
end

assign token_plus_tag = timer[1:0]==COVERY_GAP-1;


assign commit_token_plus[13:0] = token_plus_tag ? commit_covery_token_num[13:0] : 14'b0;
assign peak_token_plus[13:0]   = token_plus_tag ? peak_covery_token_num[13:0]   : 14'b0;


assign commit_token_dec[10:0] = one_bucket_ack | two_bucket_cbucket_ack ? req_bcnt[10:0] : 11'b0;
assign peak_token_dec[10:0]   = two_bucket_ack ? req_bcnt[10:0] : 11'b0;


always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        commit_bucket[19:0] <= 20'b0;
    else
        commit_bucket[19:0] <= func_bucket_cal(commit_bucket[19:0],commit_token_plus[13:0],commit_token_dec[10:0],csr_tkm_cbs[19:0]);
end



always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        peak_bucket[19:0] <= 20'b0;
    else
        peak_bucket[19:0] <= func_bucket_cal(peak_bucket[19:0],peak_token_plus[13:0],peak_token_dec[10:0],csr_tkm_pbs[19:0]);
end


//=====================judge req ack or nack=======================
assign one_bucket_mode = ~csr_tkm_mode;
assign two_bucket_mode = csr_tkm_mode;

//1 speed 1 bucket mode
assign one_bucket_ack  = one_bucket_mode & req_vld & (req_bcnt[10:0]<=commit_bucket[19:0]);
assign one_bucket_nack = ~one_bucket_ack;


//2 speed 2 bucket mode
assign two_bucket_cbucket_ack = two_bucket_mode & req_vld & (req_bcnt[10:0]<=commit_bucket[19:0]);
assign two_bucket_pbucket_ack = two_bucket_mode & req_vld & ( (req_bcnt[10:0] > commit_bucket[19:0]) | (req_bcnt[10:0]<=peak_bucket[19:0]) );
assign two_bucket_ack         = two_bucket_cbucket_ack | two_bucket_pbucket_ack;
assign two_bucket_nack        = ~two_bucket_ack;


always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        rsp_vld <= 1'b0;
    else
        rsp_vld <= req_vld;
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        rsp_ack <= 1'b0;
    else if(req_vld)
        rsp_ack <= csr_tkm_mode ? one_bucket_ack : two_bucket_ack;
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        req_bcnt_d[10:0] <= 11'b0;
    else
        req_bcnt_d[10:0] <= req_bcnt[10:0];
end

//sta
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        csr_tkm_cbc_sta[31:0] <= 32'b0;
    else if(one_bucket_ack | two_bucket_cbucket_ack)
        csr_tkm_cbc_sta[31:0] <= csr_tkm_cbc_sta[31:0] + req_bcnt_d[10:0];
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        csr_tkm_pbc_sta[31:0] <= 32'b0;
    else if(two_bucket_ack)
        csr_tkm_pbc_sta[31:0] <= csr_tkm_pbc_sta[31:0] + req_bcnt_d[10:0];
end



endmodule
